package dk.itu.mario.level;

import java.util.Random;

import dk.itu.mario.MarioInterface.Constraints;
import dk.itu.mario.MarioInterface.GamePlay;
import dk.itu.mario.MarioInterface.LevelInterface;
import dk.itu.mario.engine.sprites.SpriteTemplate;
import dk.itu.mario.engine.sprites.Enemy;
import dk.itu.mario.level.MyCustomizedLevel.PlayStyle;


public class MyOtherLevel extends Level{
        //Store information about the level
         public   int ENEMIES = 0; //the number of enemies the level contains
         public   int BLOCKS_EMPTY = 0; // the number of empty blocks
         public   int BLOCKS_COINS = 0; // the number of coin blocks
         public   int BLOCKS_POWER = 0; // the number of power blocks
         public   int COINS = 0; //These are the coins in boxes that Mario collect
         
         private int coinLevelDifficulty = 0;
         
     	enum PlayStyle { COLLECTOR, RUNNER, JUMPER, KILLER }
        private PlayStyle playStyle;
         //level type states for pcg ai
         public static final int RUNNING = 1;
         public static final int KILLING = 2;
         public static final int COLLECTING = 3;
         public int levelType = 0;

         
        private static final int ODDS_STRAIGHT = 0;
        private static final int ODDS_HILL_STRAIGHT = 1;
    private static final int ODDS_TUBES = 2;
    private static final int ODDS_JUMP = 3;
    private static final int ODDS_CANNONS = 4;
    double speedScore;
    double coinScore;
    double killScore;
    
        private int calcCoinDifficulty(int score){
                int MAXSCORE = 100;
                int scoreDiv = MAXSCORE/5;
                int difficulty = score/scoreDiv;
                
                return difficulty;
                
        }
         
         
        private static Random levelSeedRandom = new Random();
            public static long lastSeed;

            Random random;

            public GamePlay playerM;
  
            private int difficulty;
            private int type;
                private int gaps;
                
                public MyOtherLevel(int width, int height)
            {
                        super(width, height);
            }


                public MyOtherLevel(int width, int height, long seed, int difficulty, int type, GamePlay playerMetrics)
            {
                this(width, height);
                playerM = playerMetrics;
                
                
                int skillLvl = 0;
            	int totalKills = playerM.CannonBallKilled + playerM.ArmoredTurtlesKilled + playerM.ChompFlowersKilled + playerM.GoombasKilled + playerM.GreenTurtlesKilled + playerM.RedTurtlesKilled;
            	int coinsCollected = playerM.coinsCollected + playerM.coinBlocksDestroyed;
            	
            	//Normalize player metrics and score
            	//Coins (COLLECTOR)
            	coinScore = 0* (coinsCollected / playerM.totalCoins);
            	
            	//Running (RUNNER)
                double percentRunning = playerM.timeSpentRunning / playerM.totalTime;
                double percentForward = playerM.timeRunningRight / playerM.completionTime;
                 speedScore = (50 * percentRunning) + (50 * percentForward);
            	
            	//Kills (KILLER)
            	double percentKilled = totalKills / playerM.totalEnemies;
            	double fireKills = playerM.enemyKillByFire / playerM.totalEnemies;
            	double shellKills = playerM.enemyKillByKickingShell / playerM.totalEnemies;
            	killScore = 0* (70 * percentKilled) + (15 * fireKills) + (15 * shellKills);
            	
            	//Jumps (JUMPER)
            	//aimless jumps are ones that don't hit anything...
            	double percentJumps = playerM.aimlessJumps / playerM.jumpsNumber;
            	double jumpScore = 0.0*percentJumps;
            	
            	double playerScore = Math.max(coinScore,(Math.max(speedScore, jumpScore)));
            	
            	if (playerScore == speedScore) {
            		System.out.println("Player evaluated as Runner.");
            		playStyle = PlayStyle.RUNNER;
            	}
            	else if (playerScore == coinScore) {
            		System.out.println("Player evaluated as Collector.");
            		playStyle = PlayStyle.COLLECTOR;
            	}
            	else if (playerScore == killScore) {
            		System.out.println("Player evaluated as Killer.");
            		playStyle = PlayStyle.KILLER;
            	}
            	else if (playerScore == jumpScore) {
            		System.out.println("Player evaluated as Jumper.");
            		playStyle = PlayStyle.JUMPER;
            	}
            }
                
                private void buildTestLevel(){
                        //This does absolutely nothing
                        int testEnemyNum = 3;
                        //create the start location
                int length = 0;
                length += buildStraight(0, width, true);
                        
                        int straightCounter = 0;

                //create all of the medium sections
                while (length < width - 74)
                {
                    //length += buildZone(length, width - length);
                                //System.out.println("Straight placed at: " + length);
                                length += buildTestStraight(length, 10, false);
                                straightCounter = 0;
                                
                                length += buildTestHillStraight(length, 10);
                                length += buildTestTubes(length, 10);
                                length += buildTestHillStraight(length, 10);
                                length += buildTestJump(length,4,2);
                                length += buildTestCannons(length, 10);
                                System.out.println(straightCounter +" straights made so far.");
                                
                                
                                // length += buildTestStraight(length, width-length, false);
                                // length += buildTestHillStraight(length, width-length);
                                // length += buildTestJump(length, width-length);
                                // length += buildTestTubes(length, width-length);
                                // length += buildTestCannons(length, width-length);
                                
                }

                //set the end piece
                int floor = height - 1 - random.nextInt(4);

                xExit = length + 8;
                yExit = floor;

                // fills the end piece
                for (int x = length; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }

                if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
                {
                    int ceiling = 0;
                    int run = 0;
                    for (int x = 0; x < width; x++)
                    {
                        if (run-- <= 0 && x > 4)
                        {
                            ceiling = random.nextInt(4);
                            run = random.nextInt(4) + 4;
                        }
                        for (int y = 0; y < height; y++)
                        {
                            if ((x > 4 && y <= ceiling) || x < 1)
                            {
                                setBlock(x, y, GROUND);
                            }
                        }
                    }
                }
                        
                }
                
            public void creat(long seed, int difficulty, int type)
            {
                this.type = type;
                this.difficulty = difficulty;

                lastSeed = seed;
                random = new Random(seed);
                
                switch (playStyle) {
            	case RUNNER: buildSpeedLevel((int) speedScore); break;
            	case COLLECTOR: break;
            	case KILLER: buildKillLevel((int) killScore); break;
            	case JUMPER: break;
                }
                        
//                        if(levelType == 0){
//                                System.out.println("BuildingTestLevel");
//                                buildTestLevel();
//                        }
//                        else if(levelType == RUNNING){
//                                System.out.println("BuildingSpeedLevel");
//                                buildSpeedLevel((int) speedScore);
//                        }
//                        else if(levelType == KILLING){
//                                System.out.println("BuildingKillLevel");
////                                buildKillLevel((int)gpm.killScore);
//                        }
//                        else{
//                                System.out.println("BuildingCoinLevel");
////                                coinLevelDifficulty = (int)gpm.coinScore;
//                        
//                //create the start location
//                int length = 0;
//                length += buildStraight(0, width, true);
//                        
//                //create all of the medium sections
//                while (length < width - 64)
//                {
//                    //length += buildZone(length, width - length);
//                                length += buildStraight(length, width-length, false);
//                                length += buildStraight(length, width-length, false);
//                                length += buildHillStraight(length, width-length);
//                                length += buildJump(length, width-length);
//                                length += buildTubes(length, width-length);
//                                length += buildCannons(length, width-length);
//                }
//
//                //set the end piece
//                int floor = height - 1 - random.nextInt(4);
//
//                xExit = length + 8;
//                yExit = floor;
//
//                // fills the end piece
//                for (int x = length; x < width; x++)
//                {
//                    for (int y = 0; y < height; y++)
//                    {
//                        if (y >= floor)
//                        {
//                            setBlock(x, y, GROUND);
//                        }
//                    }
//                }
//
//                if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
//                {
//                    int ceiling = 0;
//                    int run = 0;
//                    for (int x = 0; x < width; x++)
//                    {
//                        if (run-- <= 0 && x > 4)
//                        {
//                            ceiling = random.nextInt(4);
//                            run = random.nextInt(4) + 4;
//                        }
//                        for (int y = 0; y < height; y++)
//                        {
//                            if ((x > 4 && y <= ceiling) || x < 1)
//                            {
//                                setBlock(x, y, GROUND);
//                            }
//                        }
//                    }
//                }
//            			}
                fixWalls();

            }
                
                private int buildTestJump(int xo, int jumpStartLen, int jumpEndLen)
            {   
                        //Note: The total length of a Test Jump section is jumpStartLen * 2 + jumpEndLen
                        //Ex: jumpStartLen = 4, jumpEndLen = 2 --> total length = 4 * 2 + 2 = 10
                
                        gaps++;
                //jl: jump length
                //js: the number of blocks that are available at either side for free
                //int js = random.nextInt(4) + 2;
                //int jl = random.nextInt(2) + 2;
                        int js = jumpStartLen;
                int jl = jumpEndLen;
                int length = js * 2 + jl;

                boolean hasStairs = random.nextInt(3) == 0;

                int floor = height - 1 - random.nextInt(4);
              //run from the start x position, for the whole length
                for (int x = xo; x < xo + length; x++)
                {
                    if (x < xo + js || x > xo + length - js - 1)
                    {
                        //run for all y's since we need to paint blocks upward
                        for (int y = 0; y < height; y++)
                        {       //paint ground up until the floor
                            if (y >= floor)
                            {
                                setBlock(x, y, GROUND);
                            }
                          //if it is above ground, start making stairs of rocks
                            else if (hasStairs)
                            {   //LEFT SIDE
                                if (x < xo + js)
                                { //we need to max it out and level because it wont
                                  //paint ground correctly unless two bricks are side by side
                                    if (y >= floor - (x - xo) + 1)
                                    {
                                        setBlock(x, y, ROCK);
                                    }
                                }
                                else
                                { //RIGHT SIDE
                                    if (y >= floor - ((xo + length) - x) + 2)
                                    {
                                        setBlock(x, y, ROCK);
                                    }
                                }
                            }
                        }
                    }
                }

                return length;
            }
                
                private int buildKillJump(int xo, int maxLength, int difficulty)
            {   gaps++;
                //jl: jump length
                //js: the number of blocks that are available at either side for free
                int js = random.nextInt(4) + 2;
                int jl = random.nextInt(2) + 2;
                int length = js * 2 + jl;
                        
                        

                boolean hasStairs = random.nextInt(3) == 0;

                int floor = height - 1 - random.nextInt(4);
                        
                        for(int i =0;i<difficulty;i++){
                                addEnemyLine(xo + 1, xo + js - 1, floor - 1 -i);
                                addEnemyLine(xo +(js*2)+1, xo + (js*2) + jl -1, floor - 1-i);
                        }
                        
              //run from the start x position, for the whole length
                for (int x = xo; x < xo + length; x++)
                {
                    if (x < xo + js || x > xo + length - js - 1)
                    {
                        //run for all y's since we need to paint blocks upward
                        for (int y = 0; y < height; y++)
                        {       //paint ground up until the floor
                            if (y >= floor)
                            {
                                setBlock(x, y, GROUND);
                            }
                          //if it is above ground, start making stairs of rocks
                            else if (hasStairs)
                            {   //LEFT SIDE
                                if (x < xo + js)
                                { //we need to max it out and level because it wont
                                  //paint ground correctly unless two bricks are side by side
                                    if (y >= floor - (x - xo) + 1)
                                    {
                                        setBlock(x, y, ROCK);
                                    }
                                }
                                else
                                { //RIGHT SIDE
                                    if (y >= floor - ((xo + length) - x) + 2)
                                    {
                                        setBlock(x, y, ROCK);
                                    }
                                }
                            }
                        }
                    }
                }

                return length;
            }

            private int buildJump(int xo, int maxLength)
            {   gaps++;
                //jl: jump length
                //js: the number of blocks that are available at either side for free
                int js = random.nextInt(4) + 2;
                int jl = random.nextInt(2) + 2;
                int length = js * 2 + jl;

                boolean hasStairs = random.nextInt(3) == 0;

                int floor = height - 1 - random.nextInt(4);
              //run from the start x position, for the whole length
                for (int x = xo; x < xo + length; x++)
                {
                    if (x < xo + js || x > xo + length - js - 1)
                    {
                        //run for all y's since we need to paint blocks upward
                        for (int y = 0; y < height; y++)
                        {       //paint ground up until the floor
                            if (y >= floor)
                            {
                                setBlock(x, y, GROUND);
                            }
                          //if it is above ground, start making stairs of rocks
                            else if (hasStairs)
                            {   //LEFT SIDE
                                if (x < xo + js)
                                { //we need to max it out and level because it wont
                                  //paint ground correctly unless two bricks are side by side
                                    if (y >= floor - (x - xo) + 1)
                                    {
                                        setBlock(x, y, ROCK);
                                    }
                                }
                                else
                                { //RIGHT SIDE
                                    if (y >= floor - ((xo + length) - x) + 2)
                                    {
                                        setBlock(x, y, ROCK);
                                    }
                                }
                            }
                        }
                    }
                }

                return length;
            }

                private int buildTestCannons(int xo, int len)
            {
                        //System.out.println("Building cannons at " + xo);
                int length = len;
                //if (length > maxLength) length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                int xCannon = xo + 1 + random.nextInt(4);
                for (int x = xo; x < xo + length; x++)
                {
                    if (x > xCannon)
                    {
                        xCannon += 2 + random.nextInt(4);
                    }
                    if (xCannon == xo + length - 1) xCannon += 10;
                    int cannonHeight = floor - random.nextInt(4) - 1;

                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                        else
                        {
                            if (x == xCannon && y >= cannonHeight)
                            {
                                if (y == cannonHeight)
                                {
                                    setBlock(x, y, (byte) (14 + 0 * 16));
                                }
                                else if (y == cannonHeight + 1)
                                {
                                    setBlock(x, y, (byte) (14 + 1 * 16));
                                }
                                else
                                {
                                    setBlock(x, y, (byte) (14 + 2 * 16));
                                }
                            }
                        }
                    }
                }
                        //System.out.println("Built Cannons. Finished at " + (xo+length));

                return length;
            }
                
                private int buildKillCannons(int xo, int maxLength,int difficulty)
            {
                int length = random.nextInt(10) + 2;
                if (length > maxLength) length = maxLength;
                        
                        

                int floor = height - 1 - random.nextInt(4);
                        
                        for(int i=0;i<difficulty;i++){
                                addEnemyLine(xo + 1, xo + length - 1, floor - 6 -i);
                        }
                        
                int xCannon = xo + 1 + random.nextInt(4);
                for (int x = xo; x < xo + length; x++)
                {
                    if (x > xCannon)
                    {
                        xCannon += 2 + random.nextInt(4);
                    }
                    if (xCannon == xo + length - 1) xCannon += 10;
                    int cannonHeight = floor - random.nextInt(4) - 1;

                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                        else
                        {
                            if (x == xCannon && y >= cannonHeight)
                            {
                                if (y == cannonHeight)
                                {
                                    setBlock(x, y, (byte) (14 + 0 * 16));
                                }
                                else if (y == cannonHeight + 1)
                                {
                                    setBlock(x, y, (byte) (14 + 1 * 16));
                                }
                                else
                                {
                                    setBlock(x, y, (byte) (14 + 2 * 16));
                                }
                            }
                        }
                    }
                }

                return length;
            }
                
            private int buildCannons(int xo, int maxLength)
            {
                int length = random.nextInt(10) + 2;
                if (length > maxLength) length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                int xCannon = xo + 1 + random.nextInt(4);
                for (int x = xo; x < xo + length; x++)
                {
                    if (x > xCannon)
                    {
                        xCannon += 2 + random.nextInt(4);
                    }
                    if (xCannon == xo + length - 1) xCannon += 10;
                    int cannonHeight = floor - random.nextInt(4) - 1;

                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                        else
                        {
                            if (x == xCannon && y >= cannonHeight)
                            {
                                if (y == cannonHeight)
                                {
                                    setBlock(x, y, (byte) (14 + 0 * 16));
                                }
                                else if (y == cannonHeight + 1)
                                {
                                    setBlock(x, y, (byte) (14 + 1 * 16));
                                }
                                else
                                {
                                    setBlock(x, y, (byte) (14 + 2 * 16));
                                }
                            }
                        }
                    }
                }

                return length;
            }
                
                private int buildTestHillStraight(int xo, int len)
            {
                int length = len;
                //if (length > maxLength) length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                for (int x = xo; x < xo + length; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }

                addEnemyLine(xo + 1, xo + length - 1, floor - 1);

                int h = floor;

                boolean keepGoing = true;

                boolean[] occupied = new boolean[length];
                while (keepGoing)
                {
                    h = h - 2 - random.nextInt(3);

                    if (h <= 0)
                    {
                        keepGoing = false;
                    }
                    else
                    {
                        int l = random.nextInt(5) + 3;
                        int xxo = random.nextInt(length - l - 2) + xo + 1;

                        if (occupied[xxo - xo] || occupied[xxo - xo + l] || occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1])
                        {
                            keepGoing = false;
                        }
                        else
                        {
                            occupied[xxo - xo] = true;
                            occupied[xxo - xo + l] = true;
                            addEnemyLine(xxo, xxo + l, h - 1);
                            if(levelType == COLLECTING){
                                if (random.nextInt(3) == 0){
                                        decorateCoins(xxo-1, xxo+l+1, h);
                                        keepGoing = false;
                                }
                            } else if (random.nextInt(4) == 0)
                            {
                                decorate(xxo - 1, xxo + l + 1, h);
                                keepGoing = false;
                            }
                            for (int x = xxo; x < xxo + l; x++)
                            {
                                for (int y = h; y < floor; y++)
                                {
                                    int xx = 5;
                                    if (x == xxo) xx = 4;
                                    if (x == xxo + l - 1) xx = 6;
                                    int yy = 9;
                                    if (y == h) yy = 8;

                                    if (getBlock(x, y) == 0)
                                    {
                                        setBlock(x, y, (byte) (xx + yy * 16));
                                    }
                                    else
                                    {
                                        if (getBlock(x, y) == HILL_TOP_LEFT) setBlock(x, y, HILL_TOP_LEFT_IN);
                                        if (getBlock(x, y) == HILL_TOP_RIGHT) setBlock(x, y, HILL_TOP_RIGHT_IN);
                                    }
                                }
                            }
                        }
                    }
                }

                return length;
            }
                
                private int buildKillHillStraight(int xo, int maxLength, int difficulty)
            {
                int length = random.nextInt(10) + 10;
                if (length > maxLength) length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                for (int x = xo; x < xo + length; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }
                        
                        for(int i = 0;i<difficulty;i++){
                                addEnemyLine(xo + 1, xo + length - 1, floor - 1 -i);
                        }
                        
                int h = floor;

                boolean keepGoing = true;

                boolean[] occupied = new boolean[length];
                while (keepGoing)
                {
                    h = h - 2 - random.nextInt(3);

                    if (h <= 0)
                    {
                        keepGoing = false;
                    }
                    else
                    {
                        int l = random.nextInt(5) + 3;
                        int xxo = random.nextInt(length - l - 2) + xo + 1;

                        if (occupied[xxo - xo] || occupied[xxo - xo + l] || occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1])
                        {
                            keepGoing = false;
                        }
                        else
                        {
                            occupied[xxo - xo] = true;
                            occupied[xxo - xo + l] = true;
                            addEnemyLine(xxo, xxo + l, h - 1);
                            if(levelType == COLLECTING){
                                if (random.nextInt(3) == 0){
                                        decorateCoins(xxo-1, xxo+l+1, h);
                                        keepGoing = false;
                                }
                            } else if (random.nextInt(4) == 0)
                            {
                                decorate(xxo - 1, xxo + l + 1, h);
                                keepGoing = false;
                            }
                            for (int x = xxo; x < xxo + l; x++)
                            {
                                for (int y = h; y < floor; y++)
                                {
                                    int xx = 5;
                                    if (x == xxo) xx = 4;
                                    if (x == xxo + l - 1) xx = 6;
                                    int yy = 9;
                                    if (y == h) yy = 8;

                                    if (getBlock(x, y) == 0)
                                    {
                                        setBlock(x, y, (byte) (xx + yy * 16));
                                    }
                                    else
                                    {
                                        if (getBlock(x, y) == HILL_TOP_LEFT) setBlock(x, y, HILL_TOP_LEFT_IN);
                                        if (getBlock(x, y) == HILL_TOP_RIGHT) setBlock(x, y, HILL_TOP_RIGHT_IN);
                                    }
                                }
                            }
                        }
                    }
                }

                return length;
            }

            private int buildHillStraight(int xo, int maxLength)
            {
                int length = random.nextInt(10) + 10;
                if (length > maxLength) length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                for (int x = xo; x < xo + length; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }

                addEnemyLine(xo + 1, xo + length - 1, floor - 1);

                int h = floor;

                boolean keepGoing = true;

                boolean[] occupied = new boolean[length];
                while (keepGoing)
                {
                    h = h - 2 - random.nextInt(3);

                    if (h <= 0)
                    {
                        keepGoing = false;
                    }
                    else
                    {
                        int l = random.nextInt(5) + 3;
                        int xxo = random.nextInt(length - l - 2) + xo + 1;

                        if (occupied[xxo - xo] || occupied[xxo - xo + l] || occupied[xxo - xo - 1] || occupied[xxo - xo + l + 1])
                        {
                            keepGoing = false;
                        }
                        else
                        {
                            occupied[xxo - xo] = true;
                            occupied[xxo - xo + l] = true;
                            addEnemyLine(xxo, xxo + l, h - 1);
                            if(levelType == COLLECTING){
                                if (random.nextInt(3) == 0){
                                        decorateCoins(xxo-1, xxo+l+1, h);
                                        keepGoing = false;
                                }
                            } else if (random.nextInt(4) == 0)
                            {
                                decorate(xxo - 1, xxo + l + 1, h);
                                keepGoing = false;
                            }
                            for (int x = xxo; x < xxo + l; x++)
                            {
                                for (int y = h; y < floor; y++)
                                {
                                    int xx = 5;
                                    if (x == xxo) xx = 4;
                                    if (x == xxo + l - 1) xx = 6;
                                    int yy = 9;
                                    if (y == h) yy = 8;

                                    if (getBlock(x, y) == 0)
                                    {
                                        setBlock(x, y, (byte) (xx + yy * 16));
                                    }
                                    else
                                    {
                                        if (getBlock(x, y) == HILL_TOP_LEFT) setBlock(x, y, HILL_TOP_LEFT_IN);
                                        if (getBlock(x, y) == HILL_TOP_RIGHT) setBlock(x, y, HILL_TOP_RIGHT_IN);
                                    }
                                }
                            }
                        }
                    }
                }

                return length;
            }

                private void addNonRandEnemyLine(int x0, int x1, int y, int enemyNum)
            {
                        int lineLength = x1-x0;
                        int remainingEnemies;
                        int remainingTiles;
                        
                        if((lineLength)<enemyNum){
                                enemyNum = lineLength;
                        }
                        
                        remainingEnemies = enemyNum;
                        remainingTiles = lineLength;
                        
                for (int x = x0; x < x1; x++)
                {
                    if ((random.nextInt(35) < 17)||(remainingEnemies==remainingTiles))
                    {
                        int type = random.nextInt(4);

                        if (difficulty < 1)
                        {
                            type = Enemy.ENEMY_GOOMBA;
                        }
                        else if (difficulty < 3)
                        {
                            type = random.nextInt(3);
                        }

                        setSpriteTemplate(x, y, new SpriteTemplate(type, random.nextInt(35) < difficulty));
                        ENEMIES++;
                                        remainingEnemies--;
                    }
                                remainingTiles--;
                }
            }
                
            private void addEnemyLine(int x0, int x1, int y)
            {
                for (int x = x0; x < x1; x++)
                {
                    if (random.nextInt(35) < difficulty + 1)
                    {
                        int type = random.nextInt(4);

                        if (difficulty < 1)
                        {
                            type = Enemy.ENEMY_GOOMBA;
                        }
                        else if (difficulty < 3)
                        {
                            type = random.nextInt(3);
                        }

                        setSpriteTemplate(x, y, new SpriteTemplate(type, random.nextInt(35) < difficulty));
                        ENEMIES++;
                    }
                }
            }
                
                private int buildTestTubes(int xo, int len)
            {
                int length = len;
                //if (length > maxLength) length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                int xTube = xo + 1 + random.nextInt(4);
                int tubeHeight = floor - random.nextInt(2) - 2;
                for (int x = xo; x < xo + length; x++)
                {
                    if (x > xTube + 1)
                    {
                        xTube += 3 + random.nextInt(4);
                        tubeHeight = floor - random.nextInt(2) - 2;
                    }
                    if (xTube >= xo + length - 2) xTube += 10;

                    if (x == xTube && random.nextInt(11) < difficulty + 1)
                    {
                        setSpriteTemplate(x, tubeHeight, new SpriteTemplate(Enemy.ENEMY_FLOWER, false));
                        ENEMIES++;
                    }

                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y,GROUND);

                        }
                        else
                        {
                            if ((x == xTube || x == xTube + 1) && y >= tubeHeight)
                            {
                                int xPic = 10 + x - xTube;

                                if (y == tubeHeight)
                                {
                                        //tube top
                                    setBlock(x, y, (byte) (xPic + 0 * 16));
                                }
                                else
                                {
                                        //tube side
                                    setBlock(x, y, (byte) (xPic + 1 * 16));
                                }
                            }
                        }
                    }
                }

                return length;
            }
                
                private int buildKillTubes(int xo, int maxLength,int difficulty)
            {
                int length = random.nextInt(10) + 5;
                if (length > maxLength) length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                int xTube = xo + 1 + random.nextInt(4);
                int tubeHeight = floor - random.nextInt(2) - 2;
                        
                        for(int i = 0;i<difficulty;i++){
                                addEnemyLine(xo + 1, xo + length - 1, floor - 6 -i);
                        }
                        
                for (int x = xo; x < xo + length; x++)
                {
                    if (x > xTube + 1)
                    {
                        xTube += 3 + random.nextInt(4);
                        tubeHeight = floor - random.nextInt(2) - 2;
                    }
                    if (xTube >= xo + length - 2) xTube += 10;

                    if (x == xTube && random.nextInt(11) < difficulty + 1)
                    {
                        setSpriteTemplate(x, tubeHeight, new SpriteTemplate(Enemy.ENEMY_FLOWER, false));
                        ENEMIES++;
                    }

                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y,GROUND);

                        }
                        else
                        {
                            if ((x == xTube || x == xTube + 1) && y >= tubeHeight)
                            {
                                int xPic = 10 + x - xTube;

                                if (y == tubeHeight)
                                {
                                        //tube top
                                    setBlock(x, y, (byte) (xPic + 0 * 16));
                                }
                                else
                                {
                                        //tube side
                                    setBlock(x, y, (byte) (xPic + 1 * 16));
                                }
                            }
                        }
                    }
                }

                return length;
            }
                
            private int buildTubes(int xo, int maxLength)
            {
                int length = random.nextInt(10) + 5;
                if (length > maxLength) length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                int xTube = xo + 1 + random.nextInt(4);
                int tubeHeight = floor - random.nextInt(2) - 2;
                for (int x = xo; x < xo + length; x++)
                {
                    if (x > xTube + 1)
                    {
                        xTube += 3 + random.nextInt(4);
                        tubeHeight = floor - random.nextInt(2) - 2;
                    }
                    if (xTube >= xo + length - 2) xTube += 10;

                    if (x == xTube && random.nextInt(11) < difficulty + 1)
                    {
                        setSpriteTemplate(x, tubeHeight, new SpriteTemplate(Enemy.ENEMY_FLOWER, false));
                        ENEMIES++;
                    }

                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y,GROUND);

                        }
                        else
                        {
                            if ((x == xTube || x == xTube + 1) && y >= tubeHeight)
                            {
                                int xPic = 10 + x - xTube;

                                if (y == tubeHeight)
                                {
                                        //tube top
                                    setBlock(x, y, (byte) (xPic + 0 * 16));
                                }
                                else
                                {
                                        //tube side
                                    setBlock(x, y, (byte) (xPic + 1 * 16));
                                }
                            }
                        }
                    }
                }

                return length;
            }

                private int buildTestStraight(int xo, int len, boolean safe)
            {
                int length = len;

                if (safe)
                        length = len;

                // if (length > maxLength)
                        // length = maxLength;

                int floor = height - 1 - random.nextInt(4);

                //runs from the specified x position to the length of the segment
                for (int x = xo; x < xo + length; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }

                if (!safe)
                {
                    if (length > 5)
                    {
                        if(levelType==COLLECTING){
                                decorateCoins(xo,xo+length,floor);
                        } else {
                                decorate(xo, xo + length, floor);
                        }
                    }
                }

                return length;
            }
                
                private int buildKillStraight(int xo, int maxLength, boolean safe, int difficulty)
            {
                int length = random.nextInt(10) + 2;

                if (safe)
                        length = 10 + random.nextInt(5);

                if (length > maxLength)
                        length = maxLength;

                int floor = height - 1 - random.nextInt(4);
                        
                        for(int i = 0;i<difficulty;i++){
                                addEnemyLine(xo + 1, xo + length - 1, floor - 1 -i);
                        }

                //runs from the specified x position to the length of the segment
                for (int x = xo; x < xo + length; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }

                if (!safe)
                {
                    if (length > 5)
                    {
                        if(levelType==COLLECTING){
                                decorateCoins(xo,xo+length,floor);
                        } else {
                                decorate(xo, xo + length, floor);
                        }
                    }
                }

                return length;
            }
                
            private int buildStraight(int xo, int maxLength, boolean safe)
            {
                int length = random.nextInt(10) + 2;

                if (safe)
                        length = 10 + random.nextInt(5);

                if (length > maxLength)
                        length = maxLength;

                int floor = height - 1 - random.nextInt(4);

                //runs from the specified x position to the length of the segment
                for (int x = xo; x < xo + length; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }

                if (!safe)
                {
                    if (length > 5)
                    {
                        if(levelType==COLLECTING){
                                decorateCoins(xo,xo+length,floor);
                        } else {
                                decorate(xo, xo + length, floor);
                        }
                    }
                }

                return length;
            }

            private void decorate(int xStart, int xLength, int floor)
            {
                //if its at the very top, just return
                if (floor < 1)
                        return;

                //        boolean coins = random.nextInt(3) == 0;
                boolean rocks = true;

                //add an enemy line above the box
                addEnemyLine(xStart + 1, xLength - 1, floor - 1);

                int s = random.nextInt(4);
                int e = random.nextInt(4);

                if (floor - 2 > 0){
                    if ((xLength - 1 - e) - (xStart + 1 + s) > 1){
                        for(int x = xStart + 1 + s; x < xLength - 1 - e; x++){
                            setBlock(x, floor - 2, COIN);
                            COINS++;
                        }
                    }
                }

                s = random.nextInt(4);
                e = random.nextInt(4);
                
                //this fills the set of blocks and the hidden objects inside them
                if (floor - 4 > 0)
                {
                    if ((xLength - 1 - e) - (xStart + 1 + s) > 2)
                    {
                        for (int x = xStart + 1 + s; x < xLength - 1 - e; x++)
                        {
                            if (rocks)
                            {
                                if (x != xStart + 1 && x != xLength - 2 && random.nextInt(3) == 0)
                                {
                                    if (random.nextInt(4) == 0)
                                    {
                                        setBlock(x, floor - 4, BLOCK_POWERUP);
                                        BLOCKS_POWER++;
                                    }
                                    else
                                    {   //the fills a block with a hidden coin
                                        setBlock(x, floor - 4, BLOCK_COIN);
                                        BLOCKS_COINS++;
                                    }
                                }
                                else if (random.nextInt(4) == 0)
                                {
                                    if (random.nextInt(4) == 0)
                                    {
                                        setBlock(x, floor - 4, (byte) (2 + 1 * 16));
                                    }
                                    else
                                    {
                                        setBlock(x, floor - 4, (byte) (1 + 1 * 16));
                                    }
                                }
                                else
                                {
                                    setBlock(x, floor - 4, BLOCK_EMPTY);
                                    BLOCKS_EMPTY++;
                                }
                            }
                        }
                    }
                }
            }

            //decorate function for coin collecting level
            //same as decorate, but coin lines are more likely to be longer
            //empty boxes are more likely to have things in them,
            //and the % of coins in boxes is based on coin score
            private void decorateCoins(int xStart, int xLength, int floor)
            {
                //if its at the very top, just return
                if (floor < 1)
                        return;

                //        boolean coins = random.nextInt(3) == 0;
                boolean rocks = true; //are there blocks to hit?

                //add an enemy line above the box
                addEnemyLine(xStart + 1, xLength - 1, floor - 1);

                int coinStart = xStart + 1 + random.nextInt(3);
                int coinEnd = xLength - 1 - random.nextInt(3);
                
                if (floor - 2 > 0){
                    if ((coinEnd) - (coinStart) > 1){
                        
                        for(int x = coinStart; x < coinEnd; x++){
                            setBlock(x, floor - 2, COIN);
                            COINS++;
                        }
                    }
                }

                int blockStart = xStart + 1 + random.nextInt(4);
                int blockEnd = xLength -1 - random.nextInt(4);
                
                //this fills the set of blocks and the hidden objects inside them
                        for(int i = 0; i<=coinLevelDifficulty;i++){
                                if (floor - 4 - i > 0)
                                {
                                        if (blockEnd - blockStart > 2)
                                        {
                                                for (int x = blockStart; x < blockEnd; x++)
                                                {
                                                        if (rocks)
                                                        {
                                                                int coinScore2 = (int)(coinScore*100);
                                                                if (x != xStart + 1 && x != xLength - 2 && random.nextInt(3) == 0)
                                                                {
                                                                        //fills question mark blocks
                                                                        if (random.nextInt(100) > coinScore2)
                                                                        {
                                                                                setBlock(x, floor - 4 - i, BLOCK_POWERUP);
                                                                                BLOCKS_POWER++;
                                                                        }
                                                                        else
                                                                        {       //the fills a block with a hidden coin
                                                                                setBlock(x, floor - 4 - i, BLOCK_COIN);
                                                                                BLOCKS_COINS++;
                                                                        }
                                                                }
                                                                //fills unmarked boxes
                                                                else if (random.nextInt(3) == 0)
                                                                {
                                                                        //powerups
                                                                        if (random.nextInt(100) > coinScore2)
                                                                        {
                                                                                setBlock(x, floor - 4, (byte) (2 + 1 * 16));
                                                                                BLOCKS_POWER++;
                                                                        }
                                                                        //coins
                                                                        else
                                                                        {
                                                                                setBlock(x, floor - 4, (byte) (1 + 1 * 16));
                                                                                BLOCKS_COINS++;
                                                                        }
                                                                }
                                                                else
                                                                {
                                                                        setBlock(x, floor - 4, BLOCK_EMPTY);
                                                                        BLOCKS_EMPTY++;
                                                                }
                                                        }
                                                }
                                        }
                                }
                        }
            }

            
            private void fixWalls()
            {
                boolean[][] blockMap = new boolean[width + 1][height + 1];

                for (int x = 0; x < width + 1; x++)
                {
                    for (int y = 0; y < height + 1; y++)
                    {
                        int blocks = 0;
                        for (int xx = x - 1; xx < x + 1; xx++)
                        {
                            for (int yy = y - 1; yy < y + 1; yy++)
                            {
                                if (getBlockCapped(xx, yy) == GROUND){
                                        blocks++;
                                }
                            }
                        }
                        blockMap[x][y] = blocks == 4;
                    }
                }
                blockify(this, blockMap, width + 1, height + 1);
            }

            private void blockify(Level level, boolean[][] blocks, int width, int height){
                int to = 0;
                if (type == LevelInterface.TYPE_CASTLE)
                {
                    to = 4 * 2;
                }
                else if (type == LevelInterface.TYPE_UNDERGROUND)
                {
                    to = 4 * 3;
                }

                boolean[][] b = new boolean[2][2];

                for (int x = 0; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        for (int xx = x; xx <= x + 1; xx++)
                        {
                            for (int yy = y; yy <= y + 1; yy++)
                            {
                                int _xx = xx;
                                int _yy = yy;
                                if (_xx < 0) _xx = 0;
                                if (_yy < 0) _yy = 0;
                                if (_xx > width - 1) _xx = width - 1;
                                if (_yy > height - 1) _yy = height - 1;
                                b[xx - x][yy - y] = blocks[_xx][_yy];
                            }
                        }

                        if (b[0][0] == b[1][0] && b[0][1] == b[1][1])
                        {
                            if (b[0][0] == b[0][1])
                            {
                                if (b[0][0])
                                {
                                    level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
                                }
                                else
                                {
                                    // KEEP OLD BLOCK!
                                }
                            }
                            else
                            {
                                if (b[0][0])
                                {
                                        //down grass top?
                                    level.setBlock(x, y, (byte) (1 + 10 * 16 + to));
                                }
                                else
                                {
                                        //up grass top
                                    level.setBlock(x, y, (byte) (1 + 8 * 16 + to));
                                }
                            }
                        }
                        else if (b[0][0] == b[0][1] && b[1][0] == b[1][1])
                        {
                            if (b[0][0])
                            {
                                //right grass top
                                level.setBlock(x, y, (byte) (2 + 9 * 16 + to));
                            }
                            else
                            {
                                //left grass top
                                level.setBlock(x, y, (byte) (0 + 9 * 16 + to));
                            }
                        }
                        else if (b[0][0] == b[1][1] && b[0][1] == b[1][0])
                        {
                            level.setBlock(x, y, (byte) (1 + 9 * 16 + to));
                        }
                        else if (b[0][0] == b[1][0])
                        {
                            if (b[0][0])
                            {
                                if (b[0][1])
                                {
                                    level.setBlock(x, y, (byte) (3 + 10 * 16 + to));
                                }
                                else
                                {
                                    level.setBlock(x, y, (byte) (3 + 11 * 16 + to));
                                }
                            }
                            else
                            {
                                if (b[0][1])
                                {
                                        //right up grass top
                                    level.setBlock(x, y, (byte) (2 + 8 * 16 + to));
                                }
                                else
                                {
                                        //left up grass top
                                    level.setBlock(x, y, (byte) (0 + 8 * 16 + to));
                                }
                            }
                        }
                        else if (b[0][1] == b[1][1])
                        {
                            if (b[0][1])
                            {
                                if (b[0][0])
                                {
                                        //left pocket grass
                                    level.setBlock(x, y, (byte) (3 + 9 * 16 + to));
                                }
                                else
                                {
                                        //right pocket grass
                                    level.setBlock(x, y, (byte) (3 + 8 * 16 + to));
                                }
                            }
                            else
                            {
                                if (b[0][0])
                                {
                                    level.setBlock(x, y, (byte) (2 + 10 * 16 + to));
                                }
                                else
                                {
                                    level.setBlock(x, y, (byte) (0 + 10 * 16 + to));
                                }
                            }
                        }
                        else
                        {
                            level.setBlock(x, y, (byte) (0 + 1 * 16 + to));
                        }
                    }
                }
            }
            
            public MyLevel clone() throws CloneNotSupportedException {

                MyLevel clone=new MyLevel(width, height);

                clone.xExit = xExit;
                clone.yExit = yExit;
                byte[][] map = getMap();
                SpriteTemplate[][] st = getSpriteTemplate();
                
                for (int i = 0; i < map.length; i++)
                        for (int j = 0; j < map[i].length; j++) {
                                clone.setBlock(i, j, map[i][j]);
                                clone.setSpriteTemplate(i, j, st[i][j]);
                }
                clone.BLOCKS_COINS = BLOCKS_COINS;
                clone.BLOCKS_EMPTY = BLOCKS_EMPTY;
                clone.BLOCKS_POWER = BLOCKS_POWER;
                clone.ENEMIES = ENEMIES;
                clone.COINS = COINS;
                
                return clone;

              }
                  
                  private void buildKillLevel(int score){
                        int MAXSCORE = 100;
                        int scoreDiv = MAXSCORE/10;
                        int difficulty = score/scoreDiv;
                        
                        int levelLength = 100 + (((width-100)/10)*difficulty);;
                        
                        int[] odds = new int[5];
                        //Ignoring difficulty for now, will implement later
                        
                        int rand = random.nextInt(100);
                        int sectionType = 0;
                        
                        odds[ODDS_STRAIGHT] = 30;
                        odds[ODDS_HILL_STRAIGHT] = 30;
                        odds[ODDS_TUBES] = 5;
                        odds[ODDS_JUMP] = 10;
                        odds[ODDS_CANNONS] = 25;
                        
                        //For testing purposes:
                        //difficulty = 10;
                        
                        //create the start location
                int length = 0;
                length += buildStraight(0, width, true);
                        
                //create all of the medium sections
                while (length < levelLength - 64)
                {
                                rand = random.nextInt(100);
                                sectionType = chooseSectionType(odds,rand);
                                switch (sectionType) {
                                        case ODDS_STRAIGHT:
                                                length += buildKillStraight(length, levelLength-length, false,difficulty);
                                                break;
                                        case ODDS_HILL_STRAIGHT:
                                                length += buildKillHillStraight(length, levelLength-length,difficulty);
                                                break;
                                        case ODDS_TUBES:
                                                length += buildKillTubes(length, levelLength-length,difficulty);
                                                break;
                                        case ODDS_JUMP:
                                                length += buildKillJump(length, levelLength-length,difficulty);
                                                break;
                                        case ODDS_CANNONS:
                                                length += buildKillCannons(length, levelLength-length,difficulty);
                                                break;
                                }
                                
                        
                    //length += buildZone(length, width - length);
                                // length += buildStraight(length, width-length, false);
                                // length += buildStraight(length, width-length, false);
                                // length += buildHillStraight(length, width-length);
                                // length += buildJump(length, width-length);
                                // length += buildTubes(length, width-length);
                                // length += buildCannons(length, width-length);
                }

                //set the end piece
                int floor = height - 1 - random.nextInt(4);

                xExit = length + 8;
                yExit = floor;

                // fills the end piece
                for (int x = length; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }

                if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
                {
                    int ceiling = 0;
                    int run = 0;
                    for (int x = 0; x < width; x++)
                    {
                        if (run-- <= 0 && x > 4)
                        {
                            ceiling = random.nextInt(4);
                            run = random.nextInt(4) + 4;
                        }
                        for (int y = 0; y < height; y++)
                        {
                            if ((x > 4 && y <= ceiling) || x < 1)
                            {
                                setBlock(x, y, GROUND);
                            }
                        }
                    }
                }
                        
                  }
                  
                  private void buildSpeedLevel(int score){
                        int MAXSCORE = 100;
                        int scoreDiv = MAXSCORE/15;
                        int difficulty = score/scoreDiv;
                        
                        int levelLength = 100 + (((width-100)/15)*difficulty);
                        
                        int[] odds = new int[5];
                        //Ignoring difficulty for now, will implement later
                        
                        
                        int sectionType = 0;
                        
                        odds[ODDS_STRAIGHT] = 40;
                        odds[ODDS_HILL_STRAIGHT] = 40;
                        odds[ODDS_TUBES] = 2*difficulty;
                        odds[ODDS_JUMP] = 3*difficulty;
                        odds[ODDS_CANNONS] = 2*difficulty;
                        
                        int rand = random.nextInt(odds[ODDS_CANNONS] + odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]);
                        
                        //create the start location
                int length = 0;
                length += buildStraight(0, width, true);
                        
                //create all of the medium sections
                while (length < levelLength - 64)
                {
                                rand = random.nextInt();
                                sectionType = chooseSectionType(odds,rand);
                                switch (sectionType) {
                                        case ODDS_STRAIGHT:
                                                length += buildStraight(length, levelLength-length, false);
                                                break;
                                        case ODDS_HILL_STRAIGHT:
                                                length += buildHillStraight(length, levelLength-length);
                                                break;
                                        case ODDS_TUBES:
                                                length += buildTubes(length, levelLength-length);
                                                break;
                                        case ODDS_JUMP:
                                                length += buildJump(length, levelLength-length);
                                                break;
                                        case ODDS_CANNONS:
                                                length += buildCannons(length, levelLength-length);
                                                break;
                                }
                                
                        
                    //length += buildZone(length, width - length);
                                // length += buildStraight(length, width-length, false);
                                // length += buildStraight(length, width-length, false);
                                // length += buildHillStraight(length, width-length);
                                // length += buildJump(length, width-length);
                                // length += buildTubes(length, width-length);
                                // length += buildCannons(length, width-length);
                }

                //set the end piece
                int floor = height - 1 - random.nextInt(4);

                xExit = length + 8;
                yExit = floor;

                // fills the end piece
                for (int x = length; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        if (y >= floor)
                        {
                            setBlock(x, y, GROUND);
                        }
                    }
                }

                if (type == LevelInterface.TYPE_CASTLE || type == LevelInterface.TYPE_UNDERGROUND)
                {
                    int ceiling = 0;
                    int run = 0;
                    for (int x = 0; x < width; x++)
                    {
                        if (run-- <= 0 && x > 4)
                        {
                            ceiling = random.nextInt(4);
                            run = random.nextInt(4) + 4;
                        }
                        for (int y = 0; y < height; y++)
                        {
                            if ((x > 4 && y <= ceiling) || x < 1)
                            {
                                setBlock(x, y, GROUND);
                            }
                        }
                    }
                }
                        
                  }
                  
                  private int chooseSectionType(int[] odds, int rand){
                        if(rand<odds[ODDS_STRAIGHT]){
                                return ODDS_STRAIGHT;
                        }
                        else if(rand<odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]){
                                return ODDS_HILL_STRAIGHT;
                        }
                        else if(rand<odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]){
                                return ODDS_TUBES;
                        }
                        else if(rand<odds[ODDS_JUMP]+odds[ODDS_TUBES]+odds[ODDS_HILL_STRAIGHT]+odds[ODDS_STRAIGHT]){
                                return ODDS_JUMP;
                        }
                        else{
                                return ODDS_CANNONS;
                        }
                  }


}